#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include <ctype.h>

#define PRERR( fmt, ... ) fprintf( stderr, fmt"\n", ##__VA_ARGS__ )

static int load_file( const char *file, char *&buf, int &len );
static void print_indent( int indent_num );

static int print_bencoding_str( const char *src, const char *end, int indent_num, int &printed_len, bool no_pr_newline = false );
static int print_bencoding_int( const char *src, const char *end, int indent_num, int &printed_len );
static int print_bencoding_dict( const char *src, const char *end, int indent_num, int &printed_len );
static int print_bencoding_list( const char *src, const char *end, int indent_num, int &printed_len );
static int print_one_bencoding_element( const char *src, const char *end, int indent_num, int &printed_len );

int main( int argc, char **argv )
{
	// load file
	char *buf = NULL;
	int buf_len = 0;
	int rc = load_file( argv[1], buf, buf_len );
	if( rc != 0 ) {
		exit( EXIT_FAILURE );
	}

	// printf( "length [%d], buf [%s]\n", buf_len, buf );
	const char *src = buf;
	const char *end = buf + buf_len;
	while( src < end ) {
		// printf( "src [%s]\n", src );
		int printed_len = 0;
		print_one_bencoding_element( src, end, 0, printed_len );
		src += printed_len;
	}

	if( NULL != buf )
		free( buf ), buf = NULL;

	return 0;
}

static int print_bencoding_int( const char *src, const char *end, int indent_num, int &printed_len )
{
	assert( 'i' == *src );
	printed_len = 0;
	src ++;
	printed_len ++;

	const char *p = src;
	if( '-' == *p )
		p++;

	while( p < end && isdigit( *p )  ) p++;
	assert( 'e' == *p );

	print_indent( indent_num );
	write( STDOUT_FILENO, src, p - src );
	write( STDOUT_FILENO, "\n", 1 );

	printed_len += p - src + 1;

	return 0;
}

static int print_bencoding_str( const char *src, const char *end, int indent_num, int &printed_len, bool no_pr_newline )
{
	assert( isdigit( *src ) );
	printed_len = 0;

	const char *p = src;
	while( p < end && isdigit( *p )  ) p++;

	assert( ':' == *p );

	int str_len = strtol( src, NULL, 10 );
	assert( str_len >= 0 );
	printed_len += p - src + 1;
	src += p - src + 1;

	print_indent( indent_num );
	write( STDOUT_FILENO, "\"", 1 );
	write( STDOUT_FILENO, src, str_len );
	write( STDOUT_FILENO, "\"", 1 );
	if( !no_pr_newline ) {
		write( STDOUT_FILENO, "\n", 1 );
	}

	printed_len += str_len;

	return 0;
}

static int print_one_bencoding_element( const char *src, const char *end, int indent_num, int &printed_len )
{
	switch( *src ) {
	case 'i':
		print_bencoding_int( src, end, indent_num, printed_len );
		break;
	case 'l':
		print_bencoding_list( src, end, indent_num, printed_len );
		break;
	case 'd':
		print_bencoding_dict( src, end, indent_num, printed_len );
		break;
	default:
		assert( isdigit( *src ) );
		print_bencoding_str( src, end, indent_num, printed_len );
	}
	return 0;
}

static int print_bencoding_list( const char *src, const char *end, int indent_num, int &printed_len )
{
	assert( 'l' == *src );
	printed_len = 0;
	src ++;
	printed_len ++;

	while( 'e' != *src && src < end ) {
		int one_printed_len = 0;
		print_one_bencoding_element( src, end, indent_num, one_printed_len );
		src += one_printed_len;
		printed_len += one_printed_len;
	}
	assert( 'e' == *src );
	printed_len ++;

	return 0;
}

static int print_bencoding_dict( const char *src, const char *end, int indent_num, int &printed_len )
{
	assert( 'd' == *src );
	printed_len = 0;
	src ++;
	printed_len ++;

	while( 'e' != *src && src < end ) {

	int one_printed_len = 0;
		print_bencoding_str( src, end, indent_num, one_printed_len, true );
		write( STDOUT_FILENO, ":\n", 2 );
		src += one_printed_len;
		printed_len += one_printed_len;

		one_printed_len = 0;
		print_one_bencoding_element( src, end, indent_num + 1, one_printed_len );
		src += one_printed_len;
		printed_len += one_printed_len;
	}
	assert( 'e' == *src );
	printed_len ++;

	return 0;
}

static void print_indent( int indent_num )
{
	const int max_tabs = 40;

	if( indent_num >= max_tabs )
		indent_num = max_tabs;

	for( int i = 0; i < indent_num; i++ ) 
		write( STDOUT_FILENO, "    ", 4 );
}

static int load_file( const char *file, char *&buf, int &len )
{
	if( NULL == file ) {
		PRERR( "invalid input" );
		return -1;
	}

	FILE *fp = fopen( file, "r" );
	if( fp == NULL ) {
		PRERR( "loadfile error, file [%s]", file );
		return -1; 
	}

	fseek( fp, 0, SEEK_END );
	len = ftell( fp );

	if( NULL != buf )
		free( buf ), buf = NULL;

	buf = (char *)malloc( len + 1 );
	memset( buf, 0, len + 1 );
	fseek( fp, 0, SEEK_SET );
	fread( buf, len, 1, fp );

	fclose( fp );
	return 0;
}

